/*
MIT License

Copyright (c) 2022 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/variable/Variable.h"
#include "simodo/bormental/DrBormental.h"
#include "simodo/variable/FunctionWrapper.h"
#include "simodo/inout/format/fmt.h"

#include <algorithm>

namespace simodo::variable
{
    Object Object::copy() const
    {
        Object record;

        for(const Variable & member : _variables)
            record.variables().push_back(member.copyVariable());

        return record;
    }

    bool Object::exists(const std::u16string & name) const
    {
        auto it = find_if(_variables.begin(), _variables.end(), [name](const Variable & v){
            return v.name() == name;
        });

        return it != _variables.end();
    }

    const Value & Object::find(const std::u16string & name) const
    {
        static const Value undef {};

        auto it = find_if(_variables.begin(), _variables.end(), [name](const Variable & v){
            return v.name() == name;
        });

        if (it == _variables.end())
            return undef;

        return it->value();  
    }

    Value & Object::find(const std::u16string & name)
    {
        static Value undef {};

        auto it = find_if(_variables.begin(), _variables.end(), [name](const Variable & v){
            return v.name() == name;
        });

        if (it == _variables.end())
            return undef;

        return it->value();
    }

    const Variable & Object::getVariableByName(const std::u16string & name) const
    {
        static const Variable undef {};

        auto it = find_if(_variables.begin(), _variables.end(), [name](const Variable & v){
            return v.name() == name;
        });

        if (it == _variables.end())
            return undef;

        return *it;  
    }

    Variable & Object::getVariableByName_mutable(const std::u16string & name)
    {
        return const_cast<Variable &>(getVariableByName(name));
    }

    const Variable & Object::getVariableByIndex(index_t index) const 
    {
        if (index >= _variables.size())
            throw bormental::DrBormental("Object::getVariableByIndex", inout::fmt("Out of index"));

        return _variables[index];
    }

    Value Object::invoke(const std::u16string & method_name, const VariableSet_t & arguments)
    {
        const Variable & function = getVariableByName(method_name);
        if (function.type() == variable::ValueType::Null) 
            return {};

        if (function.type() != variable::ValueType::Function) 
            return {};

        try
        {
            FunctionWrapper            func(function);
            VariableSet_t              args_mutable = arguments;
            VariableSetWrapper_mutable args(args_mutable);
            
            return func.invoke(args);
        }
        catch(const std::exception & e)
        {
        }
        
        return {};
    }

}